package com.itjeffrey.autocode.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itjeffrey.autocode.bo.req.MockInterfaceBO;
import com.itjeffrey.autocode.common.ACContext;
import com.itjeffrey.autocode.common.PageResult;
import com.itjeffrey.autocode.common.Result;
import com.itjeffrey.autocode.constant.SysConstant;
import com.itjeffrey.autocode.entity.MockInterfaceTbEntity;
import com.itjeffrey.autocode.enums.ExceptionEnum;
import com.itjeffrey.autocode.exception.ACException;
import com.itjeffrey.autocode.mapper.MockInterfaceTbMapper;
import com.itjeffrey.autocode.runner.RandomWordInitRunner;
import com.itjeffrey.autocode.security.JwtUserDetails;
import com.itjeffrey.autocode.service.MockInterfaceTbService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itjeffrey.autocode.util.ACBaseUtil;
import com.itjeffrey.autocode.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * MOCK接口服务实现类
 *
 * @author: admin
 * @date: 2020-12-28 23:56:08
 */
@Service
@Slf4j
public class MockInterfaceTbServiceImpl extends ServiceImpl<MockInterfaceTbMapper, MockInterfaceTbEntity> implements
        MockInterfaceTbService {

    @Resource
    private MockInterfaceTbMapper mockInterfaceTbMapper;

    @Resource
    private RandomWordInitRunner randomWordInitRunner;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result add(MockInterfaceBO mockInterfaceBO) {
        MockInterfaceTbEntity mockInterfaceTbEntity = new MockInterfaceTbEntity();
        BeanUtils.copyProperties(mockInterfaceBO, mockInterfaceTbEntity);
        mockInterfaceTbEntity.setRespParams(mockInterfaceBO.getRespParams().toJSONString());
        MockInterfaceTbEntity entity = mockInterfaceTbMapper.selectOne(new QueryWrapper<MockInterfaceTbEntity>()
                .setEntity(mockInterfaceTbEntity));
        if (entity != null) {
            throw new ACException(ExceptionEnum.MOCK_INTERFACE_HAS_EXISTS);
        }
        mockInterfaceTbEntity.setCreateTime(new Date());
        mockInterfaceTbEntity.setCreateUser(ACContext.getUserContext().getUsername());
        mockInterfaceTbMapper.insert(mockInterfaceTbEntity);
        return Result.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result mod(MockInterfaceBO mockInterfaceBO) {
        //根据接口名称、路径和方式查询
        LambdaQueryWrapper<MockInterfaceTbEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MockInterfaceTbEntity::getItfName, mockInterfaceBO.getItfName());
        queryWrapper.eq(MockInterfaceTbEntity::getItfUrl, mockInterfaceBO.getItfUrl());
        queryWrapper.eq(MockInterfaceTbEntity::getItfMethod, mockInterfaceBO.getItfMethod());
        MockInterfaceTbEntity mockInterfaceTbEntity = mockInterfaceTbMapper.selectOne(queryWrapper);
        if (mockInterfaceTbEntity == null) {
            throw new ACException(ExceptionEnum.MOCK_INTERFACE_NOT_EXISTS);
        }
        //更新
        MockInterfaceTbEntity entity = new MockInterfaceTbEntity();
        BeanUtils.copyProperties(mockInterfaceBO, entity);
        entity.setRespParams(mockInterfaceBO.getRespParams().toJSONString());
        entity.setId(mockInterfaceTbEntity.getId());
        entity.setUpdateTime(new Date());
        entity.setUpdateUser(ACContext.getUserContext().getUsername());
        int update = mockInterfaceTbMapper.updateById(entity);
        if (update != 1) {
            throw new ACException(ExceptionEnum.DATABASE_OPERATE_FAILURE);
        }
        return Result.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result del(String id) {
        mockInterfaceTbMapper.deleteById(id);
        return Result.ok();
    }

    @Override
    public Result queryOne(String id) {
        if (StringUtils.isBlank(id)) {
            throw new ACException(ExceptionEnum.SOME_PARAM_EXISTS_NULL, "id");
        }
        MockInterfaceTbEntity mockInterfaceTbEntity = mockInterfaceTbMapper.selectById(id);
        if (mockInterfaceTbEntity == null) {
            throw new ACException(ExceptionEnum.MOCK_INTERFACE_NOT_EXISTS);
        }
        MockInterfaceBO mockInterfaceBO = new MockInterfaceBO();
        BeanUtils.copyProperties(mockInterfaceTbEntity, mockInterfaceBO);
        mockInterfaceBO.setRespParams(JSONArray.parseArray(mockInterfaceTbEntity.getRespParams()));
        return Result.ok(mockInterfaceBO);
    }

    @Override
    public Result queryList(MockInterfaceBO mockInterfaceBO) {
        log.debug("查询接口列表入参 -> {}", mockInterfaceBO.toString());
        PageResult<MockInterfaceBO> pageResult = new PageResult<MockInterfaceBO>().setCurPageAndSize(mockInterfaceBO
                .calCurPage());
        LambdaQueryWrapper<MockInterfaceTbEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(mockInterfaceBO.getItfName()), MockInterfaceTbEntity::getItfName,
                SysConstant.SPECIAL_CHAR_1 + mockInterfaceBO.getItfName() + SysConstant.SPECIAL_CHAR_1);
        queryWrapper.like(StringUtils.isNotBlank(mockInterfaceBO.getItfUrl()), MockInterfaceTbEntity::getItfUrl,
                SysConstant.SPECIAL_CHAR_1 + mockInterfaceBO.getItfUrl() + SysConstant.SPECIAL_CHAR_1);
        queryWrapper.eq(StringUtils.isNotBlank(mockInterfaceBO.getItfMethod()), MockInterfaceTbEntity::getItfMethod,
                mockInterfaceBO.getItfMethod());
        queryWrapper.last(SysConstant.DB_KEYWORD_LIMIT + mockInterfaceBO.getCurPage() + SysConstant.SPECIAL_CHAR_3 +
                mockInterfaceBO.getSize()); //等价于limit 1,5 （有SQL注入风险）
        queryWrapper.orderByDesc(MockInterfaceTbEntity::getCreateTime);
        List<MockInterfaceTbEntity> mockInterfaceTbEntityList = mockInterfaceTbMapper.selectList(queryWrapper);
        List<MockInterfaceBO> mockInterfaceBOS = new ArrayList<>();
        mockInterfaceTbEntityList.forEach(t -> {
            MockInterfaceBO bo = new MockInterfaceBO();
            BeanUtils.copyProperties(t, bo);
            bo.setRespParams(JSONArray.parseArray(t.getRespParams()));
            mockInterfaceBOS.add(bo);
        });
        pageResult.setTotals(getTotalItfs(mockInterfaceBO));
        pageResult.setData(mockInterfaceBOS);
        return Result.ok(pageResult);
    }

    @Override
    public Result debugEntry(MockInterfaceBO mockInterfaceBO) {
        JwtUserDetails userContext = ACContext.getUserContext();
        log.debug("[{}]开始mock接口[{}],入参 -> {}", userContext.getUsername(), mockInterfaceBO.getItfUrl(),
                mockInterfaceBO.getRespParams());
        //查询是否存在该接口信息
        MockInterfaceTbEntity mockInterfaceTbEntity = new MockInterfaceTbEntity();
        BeanUtils.copyProperties(mockInterfaceBO, mockInterfaceTbEntity);
        MockInterfaceTbEntity entity = mockInterfaceTbMapper.selectOne(new QueryWrapper<MockInterfaceTbEntity>()
                .setEntity(mockInterfaceTbEntity));
        if (entity == null) {
            throw new ACException(ExceptionEnum.MOCK_INTERFACE_NOT_EXISTS);
        }
        //解析响应参数格式,构建返回参数
        JSONArray jsonArray = buildResponse(mockInterfaceBO.getRespParams().toJSONString());
        log.debug("[{}]mock接口[{}]完毕！", userContext.getUsername(), mockInterfaceBO.getItfUrl());
        //返回结果
        return Result.ok(jsonArray);
    }

    @Override
    public void itfExport(List<MockInterfaceBO> mockInterfaceBOS, HttpServletResponse response) {
        log.debug("[{}]正在导出接口数据，数据 -> {}", ACContext.getUserContext().getUsername(), mockInterfaceBOS.toString());
        //创建工作簿
        HSSFWorkbook workbook = new HSSFWorkbook();
        //创建sheet
        HSSFSheet sheet = workbook.createSheet("接口数据表");
        //定义表头，文件名，向表头中set表头标题
        String excelName = System.currentTimeMillis() + SysConstant.EXCELX_SUFFIX;
        HSSFRow row = sheet.createRow(0);
        String[] excelHeaders = new String[]{"接口ID", "接口名称", "接口路径", "接口方式", "创建时间", "更新时间"};
        for (int i = 0; i < excelHeaders.length; i++) {
            row.createCell(i).setCellValue(new HSSFRichTextString(excelHeaders[i])); //set入Unicode编码的字符串
        }
        //创建单元格，将查询到的接口数据set入
        int excelDataNum = 1; //表格数据行数（第一行）
        for (MockInterfaceBO mockInterfaceBO : mockInterfaceBOS) {
            HSSFRow dataRow = sheet.createRow(excelDataNum);
            dataRow.createCell(0).setCellValue(mockInterfaceBO.getId());
            dataRow.createCell(1).setCellValue(mockInterfaceBO.getItfName());
            dataRow.createCell(2).setCellValue(mockInterfaceBO.getItfUrl());
            dataRow.createCell(3).setCellValue(mockInterfaceBO.getItfMethod());
            dataRow.createCell(4).setCellValue(DateUtil.sdfDateNoTime(mockInterfaceBO.getCreateTime()));
            dataRow.createCell(5).setCellValue(DateUtil.sdfDateNoTime(mockInterfaceBO.getUpdateTime()));
            excelDataNum++;
        }
        //构建response
        try {
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(excelName, "utf-8"));
            response.flushBuffer();
            workbook.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.debug("[{}]导出接口数据完毕！", ACContext.getUserContext().getUsername());
    }

    /**
     * 解析响应参数格式,构建返回参数(要求响应参数格式必须能被解析成JSONArray)
     *
     * @param respParams jsonArray字符串
     */
    private JSONArray buildResponse(String respParams) {
        JSONArray ja = new JSONArray();
        //校验
        if (StringUtils.isBlank(respParams)) {
            return null;
        }
        try {
            JSON.parseArray(respParams);
        } catch (Exception e) {
            throw new ACException(ExceptionEnum.RESP_PARAMS_FORMAT_ERROR);
        }
        String subStr = respParams.substring(1, respParams.length() - 1);
        JSONObject jsonObject = JSONObject.parseObject(subStr, Feature.OrderedField);
        JSONArray jsonArray = new JSONArray() {{
            add(jsonObject);
        }};
        if (!respParams.contains(SysConstant.MOCK_SPECIAL_CHAR_1)) {
            return jsonArray;
        }
        //包含"$"特殊字符，则需要构建对应返回参数
        for (Object o : jsonArray) {
            JSONObject jo = new JSONObject(true);
            //解析响应参数格式
            JSONObject jsonObj;
            try {
                jsonObj = ACBaseUtil.getJsonObj(o);
            } catch (ACException e) {
                throw new ACException(e.getExceptionEnum());
            }
            analyseResp(jsonObj, jo);
            ja.add(jo);
        }
        //返回
        return ja;
    }

    /**
     * 解析JsonObject
     *
     * @param jsonObject
     */
    private void analyseResp(JSONObject jsonObject, JSONObject jo) {
        Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            // key包含"|"情况
            if (StringUtils.contains(key, SysConstant.MOCK_SPECIAL_CHAR_2) &&
                    Arrays.stream(key.split(SysConstant.SPECIAL_CHAR_2))
                            .filter(t -> StringUtils.contains(t, SysConstant.MOCK_SPECIAL_CHAR_2)).count() == 1) {
                //是否包含+
                if (StringUtils.contains(key, SysConstant.MOCK_SPECIAL_CHAR_3)) { //包含+
                    //解析+后的数字，值为key重复的次数
                    int index1 = key.indexOf(SysConstant.MOCK_SPECIAL_CHAR_2);
                    if (SysConstant.MOCK_SPECIAL_CHAR_3.equals(String.valueOf(key.charAt(index1 + 1)))) {
                        int i = Integer.valueOf(key.substring(index1 + 2)); //key重复的次数
                        if (i > 0) {
                            key = key.substring(0, key.indexOf(SysConstant.MOCK_SPECIAL_CHAR_2));
                            String str = SysConstant.SPECIAL_CHAR_2;
                            str = str + key;
                            while (i > 1) {
                                str = str + key;
                                i--;
                            }
                            analyseKeyWithoutSeparator(str, entry, jo);
                        } else {
                            throw new ACException(ExceptionEnum.MOCK_PARAM_ERROR);
                        }
                    } else {
                        throw new ACException(ExceptionEnum.MOCK_KEYWORD_PISITION_ERROR);
                    }
                } else { //不包含+
                    //解析"|"后的数字，值为对应value数组中对象的个数
                    int index2 = key.indexOf(SysConstant.MOCK_SPECIAL_CHAR_2);
                    int i = Integer.valueOf(key.substring(index2 + 1));
                    if (i > 0) {
                        if (entry.getValue() instanceof JSONArray) { //value是JsonArray
                            JSONArray jsonArray = (JSONArray) entry.getValue();
                            JSONObject o = (JSONObject) jsonArray.get(0);
                            JSONObject jsonObject1 = new JSONObject(o);
                            JSONArray ja = new JSONArray();
                            ja.add(jsonObject1);
                            while (i > 1) {
                                ja.add(jsonObject1);
                                i--;
                            }
                            //构造新的JsonArray(value)，在拼接成的jsonArray外添加一个[{"a": jsonArray}]外壳
                            JSONArray jsonArray1 = new JSONArray();
                            //遍历新构造的JsonArray
                            for (Object o1 : ja) {
                                JSONObject jsonObject2 = (JSONObject) o1;
                                JSONObject jsonObject3 = new JSONObject(true);
                                analyseResp(jsonObject2, jsonObject3);
                                jsonArray1.add(jsonObject3);
                            }
                            jo.put(key.substring(0, key.indexOf(SysConstant.MOCK_SPECIAL_CHAR_2)), jsonArray1);
                        } else {
                            throw new ACException(ExceptionEnum.MOCK_PARAM_ERROR);
                        }
                    } else {
                        throw new ACException(ExceptionEnum.MOCK_PARAM_ERROR);
                    }
                }
            } else { //不包含|情况
                analyseKeyWithoutSeparator(key, entry, jo);
            }
        }
    }

    /**
     * 解析key不包含"$"情况
     */
    private void analyseKeyWithoutSeparator(String key, Map.Entry<String, Object> entry, JSONObject jo) {
        //key不包含"$"
        if (!StringUtils.contains(key, SysConstant.MOCK_SPECIAL_CHAR_1)) {
            Object value = entry.getValue();
            String jsonstring = JSON.toJSONString(value);
            //value不含"$"
            if (!StringUtils.contains(jsonstring, SysConstant.MOCK_SPECIAL_CHAR_1)) {
                jo.put(key, value);
            } else { //value包含"$"
                //判断是否是string
                if (value instanceof String) {
                    //解析包含"$"的字符串
                    String str = analyseParams((String) value);
                    jo.put(key, str);
                }
                //判断value是否是JsonObject
                if (value instanceof JSONObject) {
                    JSONObject jsonObject1 = new JSONObject(true);
                    JSONObject jobj = (JSONObject) value;
                    analyseResp(jobj, jsonObject1);
                    jo.put(key, jsonObject1);
                }
                //判断value是否是JsonArray
                if (value instanceof JSONArray) {
                    JSONArray jArray = (JSONArray) value;
                    buildResponse(jArray.toJSONString());
                }
            }
        } else {
            throw new ACException(ExceptionEnum.RESP_PARAMS_KEY_ERROR, SysConstant.MOCK_SPECIAL_CHAR_1); //暂时不处理key包含"$"
        }
    }

    /**
     * 解析包含$的字符串
     *
     * @param value
     */
    private String analyseParams(String value) {
        //判断是否以$开头
        if (!StringUtils.startsWith(value, SysConstant.MOCK_SPECIAL_CHAR_1)) {
            throw new ACException(ExceptionEnum.PLACEHOLDER_POSITION_ERROR, SysConstant.MOCK_SPECIAL_CHAR_1);
        }
        if (value.length() < 2) {
            throw new ACException(ExceptionEnum.AFTER_PLACEHOLDER_EXISTS_NULL, SysConstant.MOCK_SPECIAL_CHAR_1);
        }
        int count = Arrays.asList(value.split(SysConstant.SPECIAL_CHAR_2)).stream().filter(t -> t.equals(SysConstant
                .MOCK_SPECIAL_CHAR_1)).collect(Collectors.toList()).size();
        if (count > 1) {
            throw new ACException(ExceptionEnum.PLACEHOLDER_COUNT_EXCEED);
        }
        //根据$后的关键字进行特殊处理
        String keyword = value.substring(1);
        //默认返回的integer、float字符串
        String intStr = String.valueOf((int) (Math.random() * 1000));
        String floatStr = String.valueOf(Math.random() * 1000);
        String emailStr = ACBaseUtil.getEmail(0, 20);
        //判断是否包含(,)
        if (StringUtils.contains(keyword, SysConstant.MOCK_SPECIAL_CHAR_4) &&
                StringUtils.contains(keyword, SysConstant.MOCK_SPECIAL_CHAR_5)) {
            int left = Integer.valueOf(keyword.substring(keyword.indexOf(SysConstant.MOCK_SPECIAL_CHAR_4) + 1,
                    keyword.indexOf(SysConstant.SPECIAL_CHAR_3)));
            int right = Integer.valueOf(keyword.substring(keyword.indexOf(SysConstant.SPECIAL_CHAR_3) + 1, keyword
                    .indexOf(SysConstant.MOCK_SPECIAL_CHAR_5)));
            //截取关键词
            String substring = keyword.substring(0, keyword.indexOf(SysConstant.MOCK_SPECIAL_CHAR_4));
            if (SysConstant.MOCK_KEYWORD_INTEGER.equals(substring)) {
                intStr = String.valueOf((int) (Math.random() * (right - left + 1) + left));
            } else if (SysConstant.MOCK_KEYWORD_FLOAT.equals(substring)) {
                //left为总位数，right为保留小数点位数
                floatStr = ACBaseUtil.generateFloat(left, right);
            } else if (SysConstant.MOCK_KEYWORD_EMAIL.equals(substring)) {
                emailStr = ACBaseUtil.getEmail(left, right);
            }
            keyword = substring;
        }
        switch (keyword) {
            case SysConstant.MOCK_KEYWORD_INTEGER: //从一段区间中获取随机整型数值
                return intStr;
            case SysConstant.MOCK_KEYWORD_FLOAT: //获取随机浮点型数值
                return floatStr;
            case SysConstant.MOCK_KEYWORD_DATE: //获取日期时间
                Calendar instance = Calendar.getInstance();
                instance.setTimeInMillis((long) ((Math.random() * 9999999999999L)));
                String dateStr = DateUtil.sdfDate(instance.getTime());
                return dateStr;
            case SysConstant.MOCK_KEYWORD_WORD: //获取英文单词(词组）
                String randomWord = new ArrayList<>(randomWordInitRunner.getRandomWordBO().getRandomWords()).get(new Random().nextInt
                        (randomWordInitRunner.getRandomWordBO().getRandomWords().size()));
                return randomWord;
            case SysConstant.MOCK_KEYWORD_NAME: //获取中文名
                return ACBaseUtil.getChineseName();
            case SysConstant.MOCK_KEYWORD_IP: //获取随机ip
                return ACBaseUtil.getRandomIp();
            case SysConstant.MOCK_KEYWORD_EMAIL: //获取随机email
                return emailStr;
            case SysConstant.MOCK_KEYWORD_REGION: //获取随机地区
                int randomInt = new Random().nextInt(SysConstant.REGIONS.length);
                return SysConstant.REGIONS[randomInt];
            default:
                return keyword;
        }
    }

    /**
     * 获取MOCK接口总个数
     *
     * @return
     */
    public Integer getTotalItfs(MockInterfaceBO mockInterfaceBO) {
        LambdaQueryWrapper<MockInterfaceTbEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(mockInterfaceBO.getItfName()), MockInterfaceTbEntity::getItfName,
                SysConstant.SPECIAL_CHAR_1 + mockInterfaceBO.getItfName() + SysConstant.SPECIAL_CHAR_1);
        queryWrapper.like(StringUtils.isNotBlank(mockInterfaceBO.getItfUrl()), MockInterfaceTbEntity::getItfUrl,
                SysConstant.SPECIAL_CHAR_1 + mockInterfaceBO.getItfUrl() + SysConstant.SPECIAL_CHAR_1);
        queryWrapper.eq(StringUtils.isNotBlank(mockInterfaceBO.getItfMethod()), MockInterfaceTbEntity::getItfMethod,
                mockInterfaceBO.getItfMethod());
        return mockInterfaceTbMapper.selectCount(queryWrapper);
    }
}